﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Net;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Xml.Serialization;
using Common.Logging;
using NPMonitor.Configuration;
using NPMonitor.DataModel;
using NPMonitor.Implementation.Internal;
using System.Threading;
using System.Globalization;

namespace NPMonitor.Helpers
{
	/// <summary>
	/// Helper utilities.
	/// </summary>
	public class Utils
    {
        public static CultureInfo SystemCulture
        {
            get
            {
                return new CultureInfo("en-GB");
            }
        }
        public static MachineInfo GetMachineInfo()
        {
            MachineInfo mi = new MachineInfo()
            {
                OSVersion = System.Environment.OSVersion.VersionString,
                MachineName = Dns.GetHostName()
            };

            ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled='TRUE'");
            ManagementObjectCollection querycollection = query.Get();

            if (querycollection.Count > 0)
            {
                foreach (ManagementObject mo in querycollection)
                {
                    mi.IPAddress = (mo["IPAddress"] as string[]).FirstOrDefault();
                    mi.MACAddress = mo["MacAddress"] as string;
                    mi.MachineDescription = mo["Description"] as string;

                    break;
                }
            }

            return mi;
        }

        public static T CloneObject<T>(T obj)
        {
            using (var ms = new MemoryStream())
            {
                var formatter = new BinaryFormatter();
                formatter.Serialize(ms, obj);
                ms.Position = 0;

                return (T)formatter.Deserialize(ms);
            }
        }

        /// <summary>
        /// 获取枚举
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T GetEnum<T>(object enumStr, T defValue, bool ignoreCase = true)
        {
            if (enumStr == null)
            {
                return defValue;
            }

            T enumVal = default(T);

            try
            {
                enumVal = (T)Enum.Parse(typeof(T), enumStr.ToString(), ignoreCase);
            }
            catch
            {
                enumVal = defValue;
            }

            return enumVal;
        }

        public static string GetFileNameByPath(string filePath)
        {
            return System.IO.Path.GetFileName(filePath);
        }

        public static string GetCurrentUser()
        {
            return "Dev";
        }

		/// <summary>
		/// Creates a string representation of the exception, including innerexception tree and stacktraces.
		/// </summary>
		/// <param name="ex">Exception to convert to string.</param>
		/// <returns></returns>
		public static string GetExceptionMessage(Exception ex)
		{
			string message =
				"MESSAGE: " + ex.Message +
				"\nSOURCE: " + ex.Source;

			message += "\nSTACKTRACE: " + ex.StackTrace;

			if (ex.InnerException != null)
				message += "\n\nINNEREXCEPTION: \n\n" + GetExceptionMessage(ex.InnerException);

			return message;
		}

		public static U GetConfigurationSection<U>() where U : ConfigurationSection
		{
			U ret = null;
			try
			{
				ret = ConfigurationManager.GetSection(typeof(U).FullName) as U;
				if (ret == null)
				{
					throw new ConfigurationErrorsException("The Configuration Section has not been setup for " + typeof(U).FullName);
				}
				return ret;
			}
			catch
			{
				throw;
			}
		}

		public static object CreateInstanceWithRequiredInterface(string typeName, string requiredInterfaceTypeName)
		{
			try
			{
				Type type = Type.GetType(typeName);
				if (type == null)
				{
					throw new TypeLoadException(string.Format("'{0} could not be loaded.", typeName));
				}
				if (type.GetInterface(requiredInterfaceTypeName) == null)
				{
					throw new TypeLoadException(string.Format("'{0}' does not support interface '{1}'.", typeName, requiredInterfaceTypeName));
				}

				return Activator.CreateInstance(Type.GetType(typeName));
			}
			catch (Exception ex)
			{
				LogManager.GetCurrentClassLogger().Error(string.Format("Failed to load : {0}", typeName), ex);
			}
			return null;
		}

		public static object CreateInstanceWithRequiredInterface(Type type, Type requiredInterface)
		{
			return CreateInstanceWithRequiredInterface(type.AssemblyQualifiedName, requiredInterface.Name);
		}

		/// <summary>
		/// Serializes the object using <see cref="XmlSerializer"/>.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="dataObject">The data object.</param>
		/// <returns></returns>
		public static string SerializeObject<T>(object dataObject) where T : class
		{
			return SerializeObject(dataObject, typeof(T));
		}

		/// <summary>
		/// Serializes the object using <see cref="XmlSerializer"/>.
		/// </summary>
		/// <param name="dataObject">The data object.</param>
		/// <param name="objectType">Type of the object.</param>
		/// <returns></returns>
		public static string SerializeObject(object dataObject, Type objectType)
		{
			try
			{
				XmlSerializer serializer = new XmlSerializer(objectType);
				StringBuilder stringBuilder = new StringBuilder();
				using (StringWriter writer = new StringWriter(stringBuilder))
				{
					serializer.Serialize(writer, dataObject);
					return stringBuilder.ToString();
				}
			}
			catch { }
			return null;
		}

		/// <summary>
		/// Deserializes the object using <see cref="XmlSerializer"/>.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="data">The data.</param>
		/// <returns></returns>
		public static T DeserializeObject<T>(string data) where T : class
		{
			return (T)DeserializeObject(data, typeof(T));
		}

		/// <summary>
		/// Deserializes the object using <see cref="XmlSerializer"/>.
		/// </summary>
		/// <param name="data">The data.</param>
		/// <param name="targetType">Type of the target.</param>
		/// <returns></returns>
		public static object DeserializeObject(string data, Type targetType)
		{
			XmlSerializer serializer = new XmlSerializer(targetType);
			using (StringReader reader = new StringReader(data))
			{
				object returnValue = serializer.Deserialize(reader);
				return returnValue;
			}
		}

        public static T DeserializeObjectFromFile<T>(string filePath)
        {
            using (StreamReader sr = new StreamReader(filePath))
            {
                XmlSerializer xz = new XmlSerializer(typeof(T));

                return (T)xz.Deserialize(sr);
            }
        }

        public static void SerializeObjectToFile(object obj, string filePath)
        {
            using (StreamWriter sw = new StreamWriter(filePath))
            {
                XmlSerializer xz = new XmlSerializer(obj.GetType());

                xz.Serialize(sw, obj);
            }
        }

        #region Database Operations

        public static bool CanConnectDb()
        {
            EFJobStoreConfigurationSection jcs = Helpers.Utils.GetConfigurationSection<EFJobStoreConfigurationSection>();

            return CanConnectDb(jcs.ConnectionStringName);
        }

        public static bool CanConnectDb(string connectionStringName)
        {
            string connectionString = GetConnectionString(connectionStringName);

            SqlConnection sqlCon = new SqlConnection(connectionString);

            try
            {
                sqlCon.Open();

                sqlCon.Close();

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static string GetConnectionString(string connectionStringName)
        {
            try
            {
                return ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString;
            }
            catch
            {
                throw;
            }
        }

        #endregion

        #region Time Operations

        private static TimeSpan? serverLocalTimeDiff = null;

        public static bool IsServerTimeAvaliable
        {
            get
            {
                if (serverLocalTimeDiff != null)
                {
                    return true;
                }
                else
                {
                    try
                    {
                        DateTime csTime = CurrentServerUtcTime;
                    } catch { }

                    return serverLocalTimeDiff != null;
                }
            }
        }

        public static DateTime CurrentServerUtcTime
        {
            get
            {
                if (serverLocalTimeDiff == null)
                {
                    try
                    {
                        using (var ctx = new DbStoreContext())
                        {
                            DateTime serverUtcTime = ctx.Database.SqlQuery<DateTime>("Select GetUtcDate()").First();

                            TimeSpan timeDiff = serverUtcTime - DateTime.UtcNow;

                            serverLocalTimeDiff = timeDiff;
                        }
                    }
                    catch
                    {
                        serverLocalTimeDiff = null;
                    }
                }

                return DateTime.UtcNow.Add(serverLocalTimeDiff.Value);
            }
        }

        public static Guid GetNewGuid()
        {
            try
            {
                using (var ctx = new DbStoreContext())
                {
                    Guid guid = ctx.Database.SqlQuery<Guid>("Select NewId()").First();
                    return guid;
                }
            }
            catch
            {
                return new Guid();
            }
        }

        /// <summary>
        /// Set local machine time
        /// </summary>
        /// <param name="dt"></param>
        public static void SetLocalTime(DateTime dt)
        {
            Win32.SystemTime st = ToSystemTime(dt);

            Win32.SetLocalTime(ref st);
        }

        public static void SetLocalUtcTime(DateTime dt)
        {
            Win32.SystemTime st = ToSystemTime(dt);

            Win32.SetSystemTime(ref st);
        }

        public static Win32.SystemTime ToSystemTime(DateTime dt)
        {
            Win32.SystemTime st = new Win32.SystemTime();

            st.Year = (short)dt.Year;
            st.Month = (short)dt.Month;
            st.DayOfWeek = (short)dt.DayOfWeek;
            st.Day = (short)dt.Day;
            st.Hour = (short)dt.Hour;
            st.Minute = (short)dt.Minute;
            st.Second = (short)dt.Second;
            st.Millisecond = (short)dt.Millisecond;

            return st;
        }

        #endregion

        #region Win32 Operations

        public static string ExecuteCmd(string cmd)
        {
            Process p = new Process();
            p.StartInfo.FileName = "cmd.exe";           //确定程序名
            p.StartInfo.Arguments = "/c " + cmd;    //确定程式命令行
            p.StartInfo.UseShellExecute = false;        //Shell的使用
            p.StartInfo.RedirectStandardInput = true;   //重定向输入
            p.StartInfo.RedirectStandardOutput = true; //重定向输出
            p.StartInfo.RedirectStandardError = true;   //重定向输出错误
            p.StartInfo.CreateNoWindow = true;          //设置置不显示示窗口
            p.Start();   //00

            //p.StandardInput.WriteLine(command);       //也可以用这种方式输入入要行的命令
            //p.StandardInput.WriteLine("exit");        //要得加上Exit要不然下一行程式

            return p.StandardOutput.ReadToEnd();        //输出出流取得命令行结果果
        }

        /// <summary>
        /// 调用命令行杀进程，可同时关闭多个IE窗口且没有提示框出现
        /// </summary>
        /// <param name="procName"></param>
        public static void KillProcess(string procName)
        {
            if (!procName.EndsWith(".exe"))
            {
                procName = procName + ".exe";
            }

            string cmdText = String.Format("taskkill /f /im {0}", procName);

            ExecuteCmd(cmdText);

            // sometime VerFault.exe will show up user a error info, kill it either
            ExecuteCmd("taskkill /f /im WerFault.exe");
        }

        public static void ExitProcess(string procName)
        {
            Process[] processArray = Process.GetProcessesByName(procName);

            foreach (Process proc in processArray)
            {
                if (!proc.HasExited && !proc.CloseMainWindow())
                {
                    proc.Kill();
                    proc.WaitForExit();
                    proc.Dispose();

                    LogManager.GetCurrentClassLogger().DebugFormat("Killed process : {0} (id: {1})");

                        //LogManager.GetCurrentClassLogger()
                        //    .DebugFormat("Killed process : {0} (id: {1}, session id: {2}, start time: {3}, exit time: {4}, total processor time: {5})", 
                        //    proc.ProcessName, proc.Id, proc.SessionId, proc.StartTime, proc.ExitTime,
                        //    String.Format("{0}:{1}:{2}", proc.TotalProcessorTime.Hours, proc.TotalProcessorTime.Minutes, proc.TotalProcessorTime.Seconds));
                    
                }
            }

            processArray = null;
        }

        #endregion
    }
}
